# 协同过滤--Item-Based CF评分预测
def predictByItem(item_similar, user_ratings, item_id):
    '''
    预测用户对给定物品的评分值
    :param item_similar: 物品两两相似度矩阵
    :param user_ratings: 用户对物品的评分 Series
    :param item_id: 要预测的物品id
    :return: 预测的评分数
    '''
    # 1.找出与给定物品相似的物品
    similar_items = item_similar[item_id].drop([item_id]).dropna()
    # 相似物品筛选规则：正相关的物品
    similar_items = similar_items.where(similar_items > 0).dropna()
    if similar_items.empty:
        return None  # 如果没有相似物品，则返回None

    # 2.从相似物品中筛选出用户评分过的物品
    ids = set(user_ratings.dropna().index) & set(similar_items.index)
    final_similar_items = similar_items.loc[list(ids)]

    # 3.结合相似度和用户评分，预测评分
    sum_up = 0  # 保存评分预测公式的分子
    sum_down = 0  # 用来保存分母
    for sim_item_id, similarity in final_similar_items.iteritems():
        sum_up += similarity * user_ratings[sim_item_id]
        sum_down += similarity

    # 计算预测的评分值
    predict_rating = sum_up / sum_down if sum_down != 0 else None
    return predict_rating


# 根据物品相似度来进行评分预测预测，实现物品推荐
def recommendItemsByItem(user_id, ratings_matrix, item_similar, k):
    '''
    推荐给定用户的物品
    :param user_id: 用户id
    :param ratings_matrix: 用户-物品评分矩阵
    :param item_similar: 物品两两相似度矩阵
    :param k: 推荐物品的数量
    :return: 推荐物品的列表
    '''
    user_ratings = ratings_matrix.loc[user_id].dropna()  # 获取用户评分过的物品
    recommendations = []
    for item_id in item_similar.index:
        # 检查用户是否已经评分过该物品
        if item_id not in user_ratings.index:
            # 如果用户未对物品评分，则进行预测
            prediction = predictByItem(item_similar, user_ratings, item_id)
            if prediction is not None:
                recommendations.append((item_id, prediction))

    # 按预测评分降序排序推荐物品
    if recommendations:  # 如果有推荐
        recommendations.sort(key=lambda x: x[1], reverse=True)
        return [item[0] for item in recommendations[:k]]  # 返回评分最高的k个物品的ID
    else:
        return []  # 如果没有推荐，则返回空列表
